Skip to content

feat: Magisk module install#127

Open
secp192k1 wants to merge 54 commits intoReVanced:devfrom
secp192k1:feat/magisk-module
Open

feat: Magisk module install#127
secp192k1 wants to merge 54 commits intoReVanced:devfrom
secp192k1:feat/magisk-module

Conversation

@secp192k1
Copy link
Copy Markdown
Member

@secp192k1 secp192k1 commented Apr 7, 2026

Closes #127

Note: I had to rollback the AGP version during local development and testing to 8.13.2 since there is a version mismatch. But this shouldn't be an issue once they match

Introduce Magisk-based installation support: add an abstract MagiskInstaller implementing install/uninstall/getInstallation logic for deploying patched APKs as Magisk modules (writes module.prop, copies patched APK to module, restarts/kills apps). Add platform-specific installers: AdbMagiskInstaller (ADB root) and LocalMagiskInstaller (local root via LocalShellCommandRunner, Closeable). Add Magisk-related constants (module paths, COPY_APK_TO_MODULE command, MAGISK_MODULE_PROP template) used by the installer.
@secp192k1 secp192k1 marked this pull request as ready for review April 7, 2026 15:28
@secp192k1 secp192k1 marked this pull request as draft April 7, 2026 16:13
Comment thread library/src/androidMain/assets/root/service.sh Outdated
Comment thread library/src/androidMain/assets/root/module.prop Outdated
Comment thread library/src/androidMain/assets/root/service.sh Outdated
rm -f "$DIR/log"

{
echo "Induction check for $package_name"
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does induction check mean

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Basically making sure that the system is ready for the app to be injected/introduced into the system by overlaying on top of the original app

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But why "induction"

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a mix of both, right now that whole name has been replaced by the watchdog and STAGE_APK variables

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Standardized naming on these commits
secp192k1@041519a
secp192k1/revanced-manager@8f8efa5

Comment thread library/src/androidMain/assets/root/service.sh Outdated
Comment thread library/src/androidMain/assets/root/service.sh Outdated
Comment thread library/src/androidMain/assets/root/service.sh Outdated
Comment thread library/src/androidMain/assets/root/service.sh Outdated
Comment thread library/src/androidMain/assets/root/service.sh Outdated
Comment thread library/src/androidMain/assets/root/service.sh Outdated
Comment thread library/src/androidMain/assets/root/service.sh Outdated
Comment thread library/src/androidMain/assets/root/service.sh Outdated
*
* @throws PackageNameRequiredException If the [Apk] does not have a package name.
*/
override suspend fun install(apk: Apk): RootInstallerResult {
Copy link
Copy Markdown
Member

@oSumAtrIX oSumAtrIX Apr 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like this doesnt handle native libs mounting, like the extractNativeLibs utils function from earlier?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like this doesnt handle native libs mounting, like the extractNativeLibs utils function from earlier?

More detailed comment here #127 (comment)
But basically since we switched to PM for mounting, its not needed, it will be needed for bind-mounting though

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think im not understanding the diff between bind and regular mount.

For mount installations (magisk also mounts no?) we always have to ensure the base apk is installed, with pm. Yes, if the patched apk introduces new libs those would have to be extracted since we dont invoke pm for the patched apk which does this step, but this would apply to mount and bind mount equally.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think im not understanding the diff between bind and regular mount.

For mount installations (magisk also mounts no?) we always have to ensure the base apk is installed, with pm. Yes, if the patched apk introduces new libs those would have to be extracted since we dont invoke pm for the patched apk which does this step, but this would apply to mount and bind mount equally.

Two different approaches:
a. RootInstaller (bind-mount): patched apk overlaid over stock apk path via mount bind. PM never sees a new install, it reuses the /libs/ it extracted from the stock apk at install time. New .so files added by a patch will throw UnsatisfiedLinkError

b. MagiskRootInstaller (module): patched apk installed fresh via pm install inside service.sh. PM handles the full install including lib extraction = no issue 😄

Native lib extraction is only a gap for the bind-mount path; Module install is already fine

override suspend fun uninstall(packageName: String): RootInstallerResult {
logger.info("Uninstalling $packageName Magisk module")

val formattedPackageName = packageName.replace('.', '_')
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In which case would . be unsafe to use here? Isnt

/my.pkg.name/ a valid folder name

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In which case would . be unsafe to use here? Isnt

/my.pkg.name/ a valid folder name

Yes its a valid folder name, but because formattedPackageName doubles as the Magisk module id in module.prop (id=revanced___FORMATTED_PKG__), which by convention should only contain alphanumeric characters and underscores
Since the module id must match the directory name under the modules dir (/data/adb/modules/), both are kept consistent and dot-free

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, so magisk says alphanumeric -> therefore folder has to as well. Hm, is this convention something magisk mentioned anywhere? Whats the reason for this convention? Its an internal name, users dont see it. I would prefer us to break this convention on the magisk side if it doesnt cause any issues there, for the sake of not having to do this quirky operation here

Copy link
Copy Markdown
Member Author

@secp192k1 secp192k1 Apr 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this convention something magisk mentioned anywhere? Whats the reason for this convention?

Docs are located here, Magisk/docs/guides.md

id has to match this regular expression: ^[a-zA-Z][a-zA-Z0-9._-]+$
ex: ✓ a_module, ✓ a.module, ✓ module-101, ✗ a module, ✗ 1_module, ✗ -a-module
This is the unique identifier of your module. You should not change it once published.

So yeah dots are allowed... Missed it completely, since they're allowed anyways, I just removed the entire formatter logic (remove dots to underscore logic)
secp192k1@d33b54c

return RootInstallation(
INSTALLED_APK_PATH(packageName)().output.ifEmpty { null },
patchedApkPath,
MOUNT_GREP(patchedApkPath)().exitCode == 0,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason we still have the mounted boolean https://github.com/ReVanced/revanced-library/pull/127/changes#r3061197338

Lmao... 2d19f45
Also the val above as useless so I moved it inside the root installation call

#!/system/bin/sh
pm uninstall "__PATCHED_PKG__"
rm -f "/data/adb/revanced/__PKG_NAME__.apk"
rm -f "/data/adb/service.d/revanced_handle_disabled___FORMATTED_PKG__.sh"
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldnt the module uninstall script delete itself too?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldnt the module uninstall script delete itself too?

Nope. Magisk runs the uninstall script and then removes the entire module directory itself, so the script is cleaned up automatically. The explicit rm -f for the handle-disabled script is necessary precisely because that file lives outside the module directory at /data/adb/service.d/ : Magisk will never touch it

cp /proc/sys/kernel/random/boot_id "${DIR}/.boot_token"

LOG="${DIR}/log"
MAX_LOG_LINES=200
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need logging here? Is it to display stuff in magisk manager?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think its fine to not have any logging code for the module script, like logging to magisk manager is fine, but no dump to a log file

Copy link
Copy Markdown
Member Author

@secp192k1 secp192k1 Apr 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need logging here? Is it to display stuff in magisk manager?

No, this logging is strictly for the magisk module, since manager wouldn't be able to access the logs and to keep it organized by category (manager logs vs module logs) its also keeps the logs separated between multiple modules

I think its fine to not have any logging code for the module script, like logging to magisk manager is fine, but no dump to a log file

Any other alternative that comes to mind?
Also keep in mind, along side with my reply above, each log file has a MAX_LOG_LINES=200

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well I would simply remove the log entirely, I understand its a nice to have for debugging purposes, but debugging code doesnt belong in production as a general rule of thumb principle. Debugging needs to be solved elsewise

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well I would simply remove the log entirely

secp192k1@3a5f949

# (install-create/write/commit) and is less prone to early-boot binder failures.
# NOTE: On Xiaomi devices (MIUI/HyperOS), pm install may still fail due to
# package verification restrictions. Manual install via ReVanced Manager
# may be required in that case.
Copy link
Copy Markdown
Member

@oSumAtrIX oSumAtrIX Apr 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Insane lol, how do we handle that in manager tbh or like how tf do you prompt the user to install the apk on Xiamoi programatically, ideally library can handle Xiomi installation business logic by itself

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inside lol, how do we handle that in manager tbh or like how tf do you prompt the user to install the apk on Xiaomi programatically, ideally library can handle Xiomi installation business logic by itself

How basically everyone else solves this issue, by the phone props, for example:

[ro.fota.oem]: [Xiaomi]
[ro.product.bootimage.manufacturer]: [Xiaomi]
[ro.product.manufacturer]: [Xiaomi]
[ro.product.odm.manufacturer]: [Xiaomi]
[ro.product.product.brand]: [Xiaomi]
[ro.product.product.manufacturer]: [Xiaomi]
[ro.product.system.brand]: [Xiaomi]
[ro.product.system.manufacturer]: [Xiaomi]
[ro.product.system_ext.brand]: [Xiaomi]
[ro.product.system_ext.manufacturer]: [Xiaomi]
[ro.product.vendor.manufacturer]: [Xiaomi]
[ro.product.vendor_dlkm.manufacturer]: [Xiaomi]

Note: Remember that these are keys from my device with the value specifically being Xiaomi, some keys like ...vendor_dlkm... might not exist on other Xiaomi devices

Also this will almost always be a hassle to completely fix since there isnt a good and reliable way to tell MIUI and HyperOS apart (without a lot of checks)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could for example join all props and do a "contains Xiaomi" check.To be honest im not really fond of us handling xiaomi bugs in revanced, or even considering them here, its more of something xiaomi would have to solve on their side

*
* 3. Some potential issue is that writing to [system app's path]/lib would fail on read-only
* system partition (/system/app, /product/app, etc)
* Right now it's assuming it can write to it - Wrong!
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cant we just mount to that directory instead of writing?

Copy link
Copy Markdown
Member Author

@secp192k1 secp192k1 Apr 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cant we just mount to that directory instead of writing?

secp192k1@99afcc7
Like this?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants